home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / spambayes / oe_mailbox.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  24.6 KB  |  799 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. """
  5. Simple Python library for Outlook Express mailbox handling, and some
  6. other Outlook Express utility functions.
  7.  
  8. Functions:
  9.     getDBXFilesList()
  10.         Returns a list containing the DBX file names for current user
  11.     getMbox(dbxPath)
  12.         Returns an mbox converted from a DBX file
  13.     getRegistryKey()
  14.         Returns the root key for current user's Outlook Express settings
  15.     getStorePath()
  16.         Returns the path where DBX files are stored for current user
  17.     train(dbxPath, isSpam)
  18.         Trains a DBX file as spam or ham through Hammie
  19. """
  20. from __future__ import generators
  21. __author__ = 'Romain Guy <romain.guy@jext.org>'
  22. __credits__ = 'All the SpamBayes folk'
  23. import binascii
  24. import os
  25. import re
  26. import struct
  27. import mailbox
  28. import msgs
  29.  
  30. try:
  31.     import cStringIO as StringIO
  32. except ImportError:
  33.     import StringIO
  34.  
  35. import sys
  36. from time import *
  37.  
  38. try:
  39.     import win32api
  40.     import win32con
  41.     import win32gui
  42.     from win32com.shell import shell, shellcon
  43. except ImportError:
  44.     win32api = None
  45.     win32con = None
  46.     win32gui = None
  47.     shell = None
  48.     shellcon = None
  49.  
  50. import hammie
  51. import oe_mailbox
  52. import mboxutils
  53. from spambayes.Options import options
  54.  
  55. class dbxFileHeader:
  56.     '''
  57.        Each Outlook Express DBX file has a file header.
  58.        This header defines many properties, only a few of which interest us.
  59.  
  60.        The only properties which are required are defined by indexes. The
  61.        indexes are static attributes of the class and their names begin with
  62.        "fh". You can access their values through the method getEntry().
  63.     '''
  64.     HEADER_SIZE = 9404
  65.     HEADER_ENTRIES = HEADER_SIZE >> 2
  66.     MAGIC_NUMBER = 0xFE12ADCFL
  67.     OFFLINE = 0x26FE9D30L
  68.     FOLDERS = 0x6F74FDC6L
  69.     POP3UIDL = 0x6F74FDC7L
  70.     FH_FILE_INFO_LENGTH = 7
  71.     FH_FIRST_FOLDER_LIST_NODE = 27
  72.     FH_LAST_FOLDER_LIST_NODE = 28
  73.     FH_MESSAGE_CONDITIONS_PTR = 34
  74.     FH_FOLDER_CONDITIONS_PTR = 35
  75.     FH_ENTRIES = 49
  76.     FH_TREE_ROOT_NODE_PTR = 57
  77.     FILE_HEADER_ENTRIES = [
  78.         (7, 'file info length'),
  79.         (9, 'pointer to the last variable segment'),
  80.         (10, 'length of a variable segment'),
  81.         (11, 'used space of the last variable segment'),
  82.         (12, 'pointer to the last tree segment'),
  83.         (13, 'length of a tree segment'),
  84.         (14, 'used space of the last tree segment'),
  85.         (15, 'pointer to the last message segment'),
  86.         (16, 'length of a message segment'),
  87.         (17, 'used space of the last message segment'),
  88.         (18, 'root pointer to the deleted message list'),
  89.         (19, 'root pointer to the deleted tree list'),
  90.         (21, 'used space in the middle sector of the file'),
  91.         (22, 'reusable space in the middle sector of the file'),
  92.         (23, 'index of the last entry in the tree'),
  93.         (27, 'pointer to the first folder list node'),
  94.         (28, 'pointer to the last folder list node'),
  95.         (31, 'used space of the file'),
  96.         (34, 'pointer to the message conditions object'),
  97.         (35, 'pointer to the folder conditions object'),
  98.         (49, 'entries in the tree'),
  99.         (50, 'entries in the 2.nd tree'),
  100.         (51, 'entries in the 3.rd tree'),
  101.         (57, 'pointer to the root node of the tree'),
  102.         (58, 'pointer to the root node of the 2.nd tree'),
  103.         (59, 'pointer to the root node of the 3.rd tree'),
  104.         (159, 'used space for indexed info objects'),
  105.         (160, 'used space for conditions objects'),
  106.         (162, 'used space for folder list objects'),
  107.         (163, 'used space for tree objects'),
  108.         (164, 'used space for message objects')]
  109.     
  110.     def __init__(self, dbxStream):
  111.         '''Initialize the DBX header by reading it directly from the passed
  112.         stream.'''
  113.         dbxStream.seek(0)
  114.         self.dbxBuffer = dbxStream.read(dbxFileHeader.HEADER_SIZE)
  115.  
  116.     
  117.     def isMessages(self):
  118.         '''Return true iff the DBX is a messages DBX.'''
  119.         if not self.isFolders() and self.isPOP3UIDL():
  120.             pass
  121.         return not self.isOffline()
  122.  
  123.     
  124.     def isFolders(self):
  125.         '''Return true if the DBX is the folders DBX.'''
  126.         return self.getEntry(1) == dbxFileHeader.FOLDERS
  127.  
  128.     
  129.     def isPOP3UIDL(self):
  130.         '''Return true if the DBX is the POP3UIDL DBX.'''
  131.         return self.getEntry(1) == dbxFileHeader.POP3UIDL
  132.  
  133.     
  134.     def isOffline(self):
  135.         '''Return true if the DBX is the offline DBX.'''
  136.         return self.getEntry(1) == dbxFileHeader.OFFLINE
  137.  
  138.     
  139.     def isValid(self):
  140.         '''Return true if the DBX is a valid DBX file.'''
  141.         return self.getEntry(0) == dbxFileHeader.MAGIC_NUMBER
  142.  
  143.     
  144.     def getHeaderBuffer(self):
  145.         '''Return the bytes buffer containing the whole header.'''
  146.         return self.dbxBuffer
  147.  
  148.     
  149.     def getEntry(self, dbxEntry):
  150.         '''Return the n-th entry as a long integer.'''
  151.         return struct.unpack('L', self.dbxBuffer[dbxEntry * 4:dbxEntry * 4 + 4])[0]
  152.  
  153.     
  154.     def getEntryAsHexStr(self, dbxEntry):
  155.         '''Return the n-th entry as an hexadecimal string.
  156.         (Little endian encoding!)'''
  157.         return '0x' + binascii.hexlify(self.dbxBuffer[dbxEntry * 4:dbxEntry * 4 + 4])
  158.  
  159.  
  160.  
  161. class dbxFileInfo:
  162.     '''
  163.       Following the DBX header there is DBX info. This part gives the name of
  164.       the folder described by the current DBX.
  165.     '''
  166.     MESSAGE_FILE_INFO = 1560
  167.     
  168.     def __init__(self, dbxStream, dbxLength):
  169.         '''Reads the DBX info part from a DBX stream.'''
  170.         dbxStream.seek(dbxFileHeader.HEADER_SIZE)
  171.         self.dbxLength = dbxLength
  172.         self.dbxBuffer = dbxStream.read(dbxLength)
  173.  
  174.     
  175.     def isFoldersInfo(self):
  176.         '''Return true if the info belongs to folders.dbx.'''
  177.         return self.dbxLength != dbxFileInfo.MESSAGE_FILE_INFO
  178.  
  179.     
  180.     def getFolderName(self):
  181.         '''Returns the folder name.'''
  182.         pass
  183.  
  184.     
  185.     def getCreationTime(self):
  186.         '''Not implemented yet.'''
  187.         if self.isFoldersInfo():
  188.             return 'Not implemented yet'
  189.         else:
  190.             return None
  191.  
  192.  
  193.  
  194. class dbxTree:
  195.     '''Stands for the tree which stores the messages in a given folder.'''
  196.     TREE_NODE_SIZE = 636
  197.     
  198.     def __init__(self, dbxStream, dbxAddress, dbxValues):
  199.         '''Reads the addresses of the stored messages.'''
  200.         self.dbxValues = [ i for i in range(dbxValues) ]
  201.  
  202.     
  203.     def __readValues(self, dbxStream, unused, dbxAddress, dbxPosition, unused2):
  204.         dbxStream.seek(dbxAddress)
  205.         dbxBuffer = dbxStream.read(dbxTree.TREE_NODE_SIZE)
  206.         count = 0
  207.         entries = self.getEntry(dbxBuffer, 4) >> 8 & 255
  208.         if self.getEntry(dbxBuffer, 2) != 0:
  209.             self._dbxTree__readValues(dbxStream, dbxAddress, self.getEntry(dbxBuffer, 2), dbxPosition, self.getEntry(dbxBuffer, 5))
  210.             count += self.getEntry(dbxBuffer, 5)
  211.         
  212.         for i in range(entries):
  213.             pos = 6 + i * 3
  214.             if self.getEntry(dbxBuffer, pos) != 0:
  215.                 count += 1
  216.                 value = dbxPosition + count
  217.                 self.dbxValues[value - 1] = self.getEntry(dbxBuffer, pos)
  218.             
  219.             if self.getEntry(dbxBuffer, pos + 1) != 0:
  220.                 self._dbxTree__readValues(dbxStream, dbxAddress, self.getEntry(dbxBuffer, pos + 1), dbxPosition + count, self.getEntry(dbxBuffer, pos + 2))
  221.                 count += self.getEntry(dbxBuffer, pos + 2)
  222.                 continue
  223.         
  224.  
  225.     
  226.     def getEntry(self, dbxBuffer, dbxEntry):
  227.         '''Return the n-th entry as a long integer.'''
  228.         return struct.unpack('L', dbxBuffer[dbxEntry * 4:dbxEntry * 4 + 4])[0]
  229.  
  230.     
  231.     def getValue(self, dbxIndex):
  232.         '''Return the address of the n-th message.'''
  233.         return self.dbxValues[dbxIndex]
  234.  
  235.  
  236.  
  237. class dbxIndexedInfo:
  238.     '''
  239.       Messages and folders mailboxes contain the "message info" and "folders
  240.       info" entities.
  241.       These entities are indexed info sequences. This is their base class.
  242.     '''
  243.     MAX_INDEX = 32
  244.     DT_NONE = 0
  245.     
  246.     def __init__(self, dbxStream, dbxAddress):
  247.         '''Reads the indexed infos from the passed stream.'''
  248.         self.dbxBodyLength = 0x0L
  249.         self.dbxObjectLength = 0x0L
  250.         self.dbxEntries = 0x0L
  251.         self.dbxCounter = 0x0L
  252.         self.dbxBuffer = []
  253.         self.dbxIndexes = 0x0L
  254.         self.dbxBegin = [ 0x0L for i in range(dbxIndexedInfo.MAX_INDEX) ]
  255.         self.dbxLength = [ i for i in self.dbxBegin ]
  256.         self.dbxAddress = dbxAddress
  257.         self._dbxIndexedInfo__readIndexedInfo(dbxStream)
  258.  
  259.     
  260.     def __readIndexedInfo(self, dbxStream):
  261.         dbxStream.seek(self.dbxAddress)
  262.         temp = dbxStream.read(12)
  263.         self.dbxBodyLength = self._dbxIndexedInfo__getEntry(temp, 1)
  264.         self.dbxObjectLength = self._dbxIndexedInfo__getEntry(temp, 2) & 65535
  265.         self.dbxEntries = self._dbxIndexedInfo__getEntry(temp, 2) >> 16 & 255
  266.         self.dbxCounter = self._dbxIndexedInfo__getEntry(temp, 1) >> 24 & 255
  267.         self.dbxBuffer = dbxStream.read(self.dbxBodyLength)
  268.         isIndirect = bool(0)
  269.         lastIndirect = 0
  270.         data = self.dbxEntries << 2
  271.         for i in range(self.dbxEntries):
  272.             value = self._dbxIndexedInfo__getEntry(self.dbxBuffer, i)
  273.             isDirect = value & 128
  274.             index = value & 127
  275.             value >>= 8
  276.             if isDirect:
  277.                 self._dbxIndexedInfo__setIndex(index, (i << 2) + 1, 3)
  278.             else:
  279.                 self._dbxIndexedInfo__setIndex(index, data + value)
  280.                 if isIndirect:
  281.                     self._dbxIndexedInfo__setEnd(lastIndirect, data + value)
  282.                     isIndirect = bool(1)
  283.                     lastIndirect = index
  284.                 
  285.             self.dbxIndexes |= 1 << index
  286.         
  287.         if isIndirect:
  288.             self._dbxIndexedInfo__setEnd(lastIndirect, self.dbxBodyLength)
  289.         
  290.  
  291.     
  292.     def __setIndex(self, dbxIndex, dbxBegin, dbxLength = 0):
  293.         if dbxIndex < dbxIndexedInfo.MAX_INDEX:
  294.             self.dbxBegin[dbxIndex] = dbxBegin
  295.             self.dbxLength[dbxIndex] = dbxLength
  296.         
  297.  
  298.     
  299.     def __setEnd(self, dbxIndex, dbxEnd):
  300.         if dbxIndex < dbxIndexedInfo.MAX_INDEX:
  301.             self.dbxLength[dbxIndex] = dbxEnd - self.dbxBegin[dbxIndex]
  302.         
  303.  
  304.     
  305.     def __getEntry(self, dbxBuffer, dbxEntry):
  306.         return struct.unpack('L', dbxBuffer[dbxEntry * 4:dbxEntry * 4 + 4])[0]
  307.  
  308.     
  309.     def getIndexText(self, dbxIndex):
  310.         '''Returns the description of the given indexed field.'''
  311.         return ''
  312.  
  313.     
  314.     def getIndexDataType(self, dbxIndex):
  315.         '''Returns the data type of the given index.'''
  316.         return self.DT_NONE
  317.  
  318.     
  319.     def getValue(self, dbxIndex):
  320.         '''Returns a tuple : (index in buffer of the info, length of the info).'''
  321.         return (self.dbxBegin[dbxIndex], self.dbxLength[dbxIndex])
  322.  
  323.     
  324.     def getValueAsLong(self, dbxIndex):
  325.         '''Returns the indexed info as a long value.'''
  326.         (data, length) = self.getValue(dbxIndex)
  327.         value = 0
  328.         if data:
  329.             value = struct.unpack('L', self.dbxBuffer[data:data + 4])[0]
  330.             if length < 4:
  331.                 value &= (1 << length << 3) - 1
  332.             
  333.         
  334.         return value
  335.  
  336.     
  337.     def getString(self, dbxIndex):
  338.         '''Returns the indexed info as a string value.'''
  339.         index = self.dbxBegin[dbxIndex]
  340.         end = index
  341.         for c in self.dbxBuffer[index:]:
  342.             if ord(c) == 0:
  343.                 break
  344.             
  345.             end += 1
  346.         
  347.         return self.dbxBuffer[index:end]
  348.  
  349.     
  350.     def getAddress(self):
  351.         return self.dbxAddress
  352.  
  353.     
  354.     def getBodyLength(self):
  355.         return self.dbxBodyLength
  356.  
  357.     
  358.     def getEntries(self):
  359.         return self.dbxEntries
  360.  
  361.     
  362.     def getCounter(self):
  363.         return self.dbxCounter
  364.  
  365.     
  366.     def getIndexes(self):
  367.         return self.dbxIndexes
  368.  
  369.     
  370.     def isIndexed(self, dbxIndex):
  371.         return self.dbxIndexes & 1 << dbxIndex
  372.  
  373.  
  374.  
  375. class dbxMessageInfo(dbxIndexedInfo):
  376.     '''
  377.       The message info structure inherits from the index info one. It just
  378.       defines extra constants which allow to access pertinent info.
  379.     '''
  380.     MI_INDEX = 0
  381.     MI_FLAGS = 1
  382.     MI_MESSAGE_ADDRESS = 4
  383.     MI_SUBJECT = 8
  384.     INDEX_LABEL = [
  385.         'message index',
  386.         'flags',
  387.         'time message created/send',
  388.         'body lines',
  389.         'message address',
  390.         'original subject',
  391.         'time message saved',
  392.         'message id',
  393.         'subject',
  394.         'sender eMail address and name',
  395.         'answered to message id',
  396.         'server/newsgroup/message number',
  397.         'server',
  398.         'sender name',
  399.         'sender eMail address',
  400.         'id 0f',
  401.         'message priority',
  402.         'message text length',
  403.         'time message created/received',
  404.         'receiver name',
  405.         'receiver eMail address',
  406.         'id 15',
  407.         'id 16',
  408.         'id 17',
  409.         'id 18',
  410.         'id 19',
  411.         'OE account name',
  412.         'OE account registry key',
  413.         'message text structure',
  414.         'id 1d',
  415.         'id 1e',
  416.         'id 1f']
  417.     DT_NONE = 0
  418.     DT_INT4 = 1
  419.     DT_STRING = 2
  420.     DT_DATE_TIME = 3
  421.     DT_DATA = 4
  422.     INDEX_DATA_TYPE = [
  423.         DT_INT4,
  424.         DT_INT4,
  425.         DT_DATE_TIME,
  426.         DT_INT4,
  427.         DT_INT4,
  428.         DT_STRING,
  429.         DT_DATE_TIME,
  430.         DT_STRING,
  431.         DT_STRING,
  432.         DT_STRING,
  433.         DT_STRING,
  434.         DT_STRING,
  435.         DT_STRING,
  436.         DT_STRING,
  437.         DT_STRING,
  438.         DT_NONE,
  439.         DT_INT4,
  440.         DT_INT4,
  441.         DT_DATE_TIME,
  442.         DT_STRING,
  443.         DT_STRING,
  444.         DT_NONE,
  445.         DT_INT4,
  446.         DT_NONE,
  447.         DT_INT4,
  448.         DT_INT4,
  449.         DT_STRING,
  450.         DT_STRING,
  451.         DT_DATA,
  452.         DT_NONE,
  453.         DT_NONE,
  454.         DT_NONE]
  455.     
  456.     def getIndexText(self, dbxIndex):
  457.         return dbxMessageInfo.INDEX_LABEL[dbxIndex]
  458.  
  459.     
  460.     def getIndexDataType(self, dbxIndex):
  461.         return dbxMessageInfo.INDEX_DATA_TYPE[dbxIndex]
  462.  
  463.  
  464.  
  465. class dbxMessage:
  466.     
  467.     def __init__(self, dbxStream, dbxAddress):
  468.         self.dbxAddress = dbxAddress
  469.         self.dbxText = ''
  470.         self.dbxLength = 0x0L
  471.         self._dbxMessage__readMessageText(dbxStream)
  472.  
  473.     
  474.     def __getEntry(self, dbxBuffer, dbxEntry):
  475.         if len(dbxBuffer) < dbxEntry * 4 + 4:
  476.             return None
  477.         
  478.         return struct.unpack('L', dbxBuffer[dbxEntry * 4:dbxEntry * 4 + 4])[0]
  479.  
  480.     
  481.     def __readMessageText(self, dbxStream):
  482.         address = self.dbxAddress
  483.         header = ''
  484.         while address:
  485.             dbxStream.seek(address)
  486.             header = dbxStream.read(16)
  487.             self.dbxLength += self._dbxMessage__getEntry(header, 2)
  488.             address = self._dbxMessage__getEntry(header, 3)
  489.             continue
  490.             self
  491.         pos = ''
  492.         address = self.dbxAddress
  493.         while address:
  494.             dbxStream.seek(address)
  495.             header = dbxStream.read(16)
  496.             pos += dbxStream.read(self._dbxMessage__getEntry(header, 2))
  497.             address = self._dbxMessage__getEntry(header, 3)
  498.         self.dbxText = pos
  499.  
  500.     
  501.     def getText(self):
  502.         return self.dbxText
  503.  
  504.  
  505.  
  506. def convertToMbox(content):
  507.     """Check if the given buffer is in a non-mbox format, and convert it
  508.     into mbox format if so.  If it's already an mbox, return it unchanged.
  509.     """
  510.     dbxStream = StringIO.StringIO(content)
  511.     header = dbxFileHeader(dbxStream)
  512.     if header.isValid() and header.isMessages():
  513.         file_info_len = dbxFileHeader.FH_FILE_INFO_LENGTH
  514.         fh_entries = dbxFileHeader.FH_ENTRIES
  515.         fh_ptr = dbxFileHeader.FH_TREE_ROOT_NODE_PTR
  516.         info = dbxFileInfo(dbxStream, header.getEntry(file_info_len))
  517.         entries = header.getEntry(fh_entries)
  518.         address = header.getEntry(fh_ptr)
  519.         if address and entries:
  520.             tree = dbxTree(dbxStream, address, entries)
  521.             dbxBuffer = []
  522.             for i in range(entries):
  523.                 address = tree.getValue(i)
  524.                 messageInfo = dbxMessageInfo(dbxStream, address)
  525.                 if messageInfo.isIndexed(dbxMessageInfo.MI_MESSAGE_ADDRESS):
  526.                     address = dbxMessageInfo.MI_MESSAGE_ADDRESS
  527.                     messageAddress = messageInfo.getValueAsLong(address)
  528.                     message = dbxMessage(dbxStream, messageAddress)
  529.                     dbxBuffer.append('From spambayes@spambayes.org %s\n%s' % (strftime('%a %b %d %H:%M:%S MET %Y', gmtime()), message.getText()))
  530.                     continue
  531.             
  532.             content = ''.join(dbxBuffer)
  533.         
  534.     
  535.     dbxStream.close()
  536.     return content
  537.  
  538.  
  539. def OEIdentityKeys():
  540.     '''Return the OE identity keys.
  541.  
  542.     Tested with Outlook Express 6.0 with Windows XP.'''
  543.     if win32api is None:
  544.         raise ImportError('pywin32 not installed')
  545.     
  546.     reg = win32api.RegOpenKeyEx(win32con.HKEY_USERS, '')
  547.     user_index = 0
  548.     while True:
  549.         
  550.         try:
  551.             user_name = '%s\\Identities' % (win32api.RegEnumKey(reg, user_index),)
  552.         except win32api.error:
  553.             break
  554.  
  555.         user_index += 1
  556.         
  557.         try:
  558.             user_key = win32api.RegOpenKeyEx(win32con.HKEY_USERS, user_name)
  559.         except win32api.error:
  560.             continue
  561.  
  562.         identity_index = 0
  563.         while True:
  564.             
  565.             try:
  566.                 identity_name = win32api.RegEnumKey(user_key, identity_index)
  567.             except win32api.error:
  568.                 break
  569.  
  570.             identity_index += 1
  571.             subkey_name = '%s\\%s\\%s' % (user_name, identity_name, 'Software\\Microsoft\\Outlook Express\\5.0')
  572.             
  573.             try:
  574.                 subkey = win32api.RegOpenKeyEx(win32con.HKEY_USERS, subkey_name, 0, win32con.KEY_READ)
  575.             except win32api.error:
  576.                 continue
  577.  
  578.             yield subkey
  579.  
  580.  
  581. def OECurrentUserKey():
  582.     '''Returns the root registry key for current user Outlook
  583.     Express settings.'''
  584.     if win32api is None:
  585.         raise ImportError('pywin32 not installed')
  586.     
  587.     key = 'Identities'
  588.     reg = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, key)
  589.     id = win32api.RegQueryValueEx(reg, 'Default User ID')[0]
  590.     subKey = '%s\\%s\\Software\\Microsoft\\Outlook Express\\5.0' % (key, id)
  591.     return subKey
  592.  
  593.  
  594. def OEStoreRoot():
  595.     '''Return the path to the Outlook Express Store Root.
  596.  
  597.     Tested with Outlook Express 6.0 with Windows XP.'''
  598.     subKey = OECurrentUserKey()
  599.     reg = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, subKey)
  600.     path = win32api.RegQueryValueEx(reg, 'Store Root')[0]
  601.     UserDirectory = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, 0, 0)
  602.     parts = UserDirectory.split(os.sep)
  603.     UserProfile = os.sep.join(parts[:-2])
  604.     return path.replace('%UserProfile%', UserProfile)
  605.  
  606.  
  607. def OEDBXFilesList():
  608.     '''Returns a list of DBX files for current user.'''
  609.     path = OEStoreRoot()
  610.     dbx_re = re.compile('.+\\.dbx')
  611.     dbxs = _[1]
  612.     return dbxs
  613.  
  614.  
  615. def OEAccountKeys(permission = None):
  616.     '''Return registry keys for each of the OE mail accounts, along
  617.     with information about what type of mail account it is.'''
  618.     if permission is None:
  619.         permission = win32con.KEY_READ | win32con.KEY_SET_VALUE
  620.     
  621.     possible_root_keys = []
  622.     if sys.getwindowsversion()[0] >= 4:
  623.         possible_root_keys = [
  624.             'Software\\Microsoft\\Internet Account Manager\\Accounts']
  625.     else:
  626.         possible_root_keys = oe_mailbox.OEIdentityKeys()
  627.     for key in possible_root_keys:
  628.         reg = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, key)
  629.         account_index = 0
  630.         while True:
  631.             account = { }
  632.             
  633.             try:
  634.                 subkey_name = '%s\\%s' % (key, win32api.RegEnumKey(reg, account_index))
  635.             except win32api.error:
  636.                 break
  637.  
  638.             account_index += 1
  639.             index = 0
  640.             subkey = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, subkey_name, 0, permission)
  641.             while True:
  642.                 
  643.                 try:
  644.                     (name, value, typ) = win32api.RegEnumValue(subkey, index)
  645.                 except win32api.error:
  646.                     break
  647.  
  648.                 account[name] = (value, typ)
  649.                 index += 1
  650.             if account.has_key('POP3 Server'):
  651.                 yield ('POP3', subkey, account)
  652.                 continue
  653.             if account.has_key('IMAP Server'):
  654.                 yield ('IMAP4', subkey, account)
  655.                 continue
  656.     
  657.  
  658.  
  659. def OEIsInstalled():
  660.     '''Return True if Outlook Express appears to be installed,
  661.     and in use (I think if sys.platform == "win32" would say if
  662.     it was installed at all).'''
  663.     if len(list(OEAccountKeys)) > 0:
  664.         return True
  665.     
  666.     return False
  667.  
  668.  
  669. class OEMsg(msgs.Msg):
  670.     
  671.     def __init__(self, guts, id):
  672.         self.tag = id
  673.         self.guts = guts
  674.  
  675.  
  676.  
  677. class OEMsgStream(msgs.MsgStream):
  678.     
  679.     def __init__(self, tag, dbxes, keep = None):
  680.         msgs.MsgStream.__init__(self, tag, dbxes, keep)
  681.  
  682.     
  683.     def produce(self):
  684.         if self.keep is None:
  685.             for dbx in self.directories:
  686.                 folder = convertToMbox(file(dbx))
  687.                 all = folder.split('\nFrom ')
  688.                 count = 0
  689.                 for msg in all:
  690.                     id = '%s::%s' % (dbx, count)
  691.                     count += 1
  692.                     yield OEMsg(msg, id)
  693.                 
  694.             
  695.             return None
  696.         
  697.         for directory in self.directories:
  698.             folder = convertToMbox(file(dbx))
  699.             all = folder.split('\nFrom ')
  700.             random.seed(hash(max(all)) ^ SEED)
  701.             random.shuffle(all)
  702.             del all[self.keep:]
  703.             all.sort()
  704.             count = 0
  705.             for msg in all:
  706.                 id = '%s::%s' % (dbx, count)
  707.                 count += 1
  708.                 yield OEMsg(msg, id)
  709.             
  710.         
  711.  
  712.  
  713.  
  714. class OEHamStream(msgs.HamStream):
  715.     
  716.     def __init__(self, tag, dbxes, train = 0):
  717.         msgs.HamStream.__init__(self, tag, dbxes, train)
  718.  
  719.  
  720.  
  721. class OESpamStream(msgs.SpamStream):
  722.     
  723.     def __init__(self, tag, dbxes, train = 0):
  724.         msgs.SpamStream.__init__(self, tag, dbxes, train)
  725.  
  726.  
  727.  
  728. def test():
  729.     import sys
  730.     import getopt
  731.     
  732.     try:
  733.         (opts, args) = getopt.getopt(sys.argv[1:], 'hp')
  734.     except getopt.error:
  735.         msg = None
  736.         print >>sys.stderr, str(msg) + '\n\n' + __doc__
  737.         sys.exit()
  738.  
  739.     print_message = False
  740.     for opt, arg in opts:
  741.         if opt == '-h':
  742.             print >>sys.stderr, __doc__
  743.             sys.exit()
  744.             continue
  745.         if opt == '-p':
  746.             print_message = True
  747.             continue
  748.     
  749.     MAILBOX_DIR = OEStoreRoot()
  750.     files = [ os.path.join(MAILBOX_DIR, f) for f in OEDBXFilesList() ]
  751.     for file in files:
  752.         
  753.         try:
  754.             print 
  755.             print file
  756.             dbx = open(file, 'rb', 0)
  757.             header = dbxFileHeader(dbx)
  758.             print 'IS VALID DBX  :', header.isValid()
  759.             if header.isMessages():
  760.                 info = dbxFileInfo(dbx, header.getEntry(dbxFileHeader.FH_FILE_INFO_LENGTH))
  761.                 print 'MAILBOX NAME  :', info.getFolderName()
  762.                 print 'CREATION TIME :', info.getCreationTime()
  763.                 entries = header.getEntry(dbxFileHeader.FH_ENTRIES)
  764.                 address = header.getEntry(dbxFileHeader.FH_TREE_ROOT_NODE_PTR)
  765.                 if address and entries:
  766.                     tree = dbxTree(dbx, address, entries)
  767.                 
  768.                 for i in range(entries):
  769.                     address = tree.getValue(i)
  770.                     messageInfo = dbxMessageInfo(dbx, address)
  771.                     if messageInfo.isIndexed(dbxMessageInfo.MI_MESSAGE_ADDRESS):
  772.                         messageAddress = messageInfo.getValueAsLong(dbxMessageInfo.MI_MESSAGE_ADDRESS)
  773.                         message = dbxMessage(dbx, messageAddress)
  774.                         if print_message:
  775.                             print 
  776.                             print 'Message :', messageInfo.getString(dbxMessageInfo.MI_SUBJECT)
  777.                             print '=' * (len(messageInfo.getString(dbxMessageInfo.MI_SUBJECT)) + 9)
  778.                             print 
  779.                             print message.getText()
  780.                         
  781.                     print_message
  782.                 
  783.             
  784.             dbx.close()
  785.         continue
  786.         except Exception:
  787.             []
  788.             strerror = []
  789.             []
  790.             print strerror
  791.             continue
  792.         
  793.  
  794.     
  795.  
  796. if __name__ == '__main__':
  797.     test()
  798.  
  799.